﻿using Games.Common;
using Games.Data;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace Games.Eight
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    partial class GamePage : ContentPage
    {
        IDataBase database = DependencyService.Get<IDataBase>();
        Game game;
        GameInfo gameInfo;
        const int CellSpacing = 2;
        int cellSize;
        double layoutWidth, layoutHeight;
        EightLevel boxLevel;
        ScrollView view;
        IDictionary<string, Button> buttons;
        internal GamePage(GameInfo gameInfo)
        {
            InitializeComponent();
            BindingContext = this.gameInfo = gameInfo;
            game = new Game();
            LoadAsync().Wait();
            database.CreateTable<EightRecord>();
        }
        async Task LoadAsync()
        {
            var gameArgs = await database.GetFirstOrDefaultAsync<GameArgs>(x => x.GameID == gameInfo.ID).ConfigureAwait(false);
            if(gameArgs != null)
            {
                boxLevel = BaseArgs.Convert<EightLevel>(gameArgs);
            }
            else
            {
                boxLevel = new EightLevel()
                {
                    GameID = gameInfo.ID,
                    //Version = gameInfo.Version,
                    Level = 3,
                };
            }
        }
        async Task SaveArgsAsync()
        {
            await database.SaveAsync(boxLevel.ToGameArgs());
        }
        void OnLayoutSizeChanged(object sender, EventArgs args)
        {
            Layout layout = sender as Layout;
            layoutWidth = layout.Width;
            layoutHeight = layout.Height;
            InitLayout();
        }

        void InitLayout(bool isReload = false)
        {
            if(!isReload || game.IsFinished)
            {
                game.Init(boxLevel.Level);
                gameInfo.Level = boxLevel.Level;
            }
            else
            {
                game.Reload();
            }
            gameInfo.StepCount = 0;
            var sizew = (int)((layoutWidth - 6 + CellSpacing) / game.Size);
            var sizeh = (int)((layoutHeight - 6 + CellSpacing) / game.Size);
            cellSize = Math.Min(sizew, sizeh);
            var laySize = game.Size * cellSize - CellSpacing;
            board.WidthRequest = board.HeightRequest = laySize + 6;

            var xMargin = (layoutWidth - laySize - 6) / 2.0;
            var yMargin = (layoutHeight - laySize - 6) / 2.0;
            board.Margin = new Thickness(xMargin, yMargin);
            absoluteLayout.WidthRequest = absoluteLayout.HeightRequest = laySize;
            absoluteLayout.Margin = new Thickness(3);
            var count = game.Size * game.Size - 1;

            while(absoluteLayout.Children.Count > count)
            {
                absoluteLayout.Children.RemoveAt(0);
            }

            while(absoluteLayout.Children.Count < count)
            {
                GameCell lifeCell = new GameCell();
                absoluteLayout.Children.Add(lifeCell);
            }

            var index = 0;
            foreach(var val in game.Value)
            {
                if(val > 0)
                {
                    GameCell cell = (GameCell)absoluteLayout.Children[val - 1];
                    cell.Col = index % game.Size;
                    cell.Row = index / game.Size;
                    cell.Text = val.ToString();
                    cell.FontSize = cellSize / 2;
                    Rectangle rect = new Rectangle(cell.Col * cellSize,
                                                   cell.Row * cellSize,
                                                   cellSize - CellSpacing,
                                                   cellSize - CellSpacing);
                    AbsoluteLayout.SetLayoutBounds(cell, rect);
                    cell.Show();
                }
                index++;
            }
            TimeCounter.Start();
        }

        async void OnSwiped(object sender, SwipedEventArgs e)
        {
            if(game.IsFinished)
            {
                return;
            }
            var flag = await game.Move(e.Direction.ToDirection(), Push);
            if(flag)
            {
                gameInfo.StepCount++;
            }
            if(game.IsFinished)
            {
                await ShowFinish();
            }
        }

        async void TapGestureRecognizer_Tapped(object sender, EventArgs e)
        {
            if(game.IsFinished || gameInfo.StepCount <= 0)
            {
                return;
            }
            await game.Back(Push);
            gameInfo.StepCount--;
        }

        Task Push(int index, Direction direction)
        {
            var cell = (Label)absoluteLayout.Children[index - 1];
            var fromBound = AbsoluteLayout.GetLayoutBounds(cell);
            var fromx = fromBound.X;
            var fromy = fromBound.Y;
            var tox = fromx;
            var toy = fromy;
            switch(direction)
            {
                case Direction.Up:
                    toy -= cellSize;
                    break;
                case Direction.Down:
                    toy += cellSize;
                    break;
                case Direction.Left:
                    tox -= cellSize;
                    break;
                case Direction.Right:
                    tox += cellSize;
                    break;
            }
            var taskCompletionSource = new TaskCompletionSource<bool>();

            cell.Animate<Rectangle>(
                "Push",
                t => new Rectangle(fromx + t * (tox - fromx), fromy + t * (toy - fromy), fromBound.Width, fromBound.Height),
                r => AbsoluteLayout.SetLayoutBounds(cell, r),
                16,
                125,
                Easing.Linear,
                (r, b) => taskCompletionSource.SetResult(b)
            );
            return taskCompletionSource.Task;
        }

        async Task ShowFinish()
        {
            var dur = TimeCounter.Stop();
            var record = await SaveRecordAsync();
            await DisplayAlert("恭喜", record.Message, "OK");
        }
        async Task<Record> SaveRecordAsync()
        {
            var dur = TimeCounter.Stop();
            var click = gameInfo.StepCount;
            var cRecord = new EightRecord()
            {
                Level = game.Size,
                GameID = gameInfo.ID,
                AllClickCount = gameInfo.StepCount,
                CreateTime = DateTime.Now,
                Duration = dur
            };
            await database.SaveRecordByDiff(cRecord, 20,
                o => o.Level == cRecord.Level,
                o => o.OrderBy(x => x.AllClickCount),
                o => o.AllClickCount > cRecord.AllClickCount || (o.AllClickCount == cRecord.AllClickCount && o.Duration > cRecord.Duration)
            );
            return cRecord.Convert();
        }
        async void LevelClicked(object sender, EventArgs args)
        {
            if(!mask.Contains("levels"))
            {
                var diffs = Enumerable.Range(3, 5).Select(x => $"{x} * {x}").ToList();
                (view, buttons) = ControlUtil.GetLevelGrid(diffs, -1, 1, LevelSelect);
                mask.SetSize("levels", 0.8, 0.64);
                mask.Add("levels", "选择难度", view);
            }
            mask.ShowWithCancel("levels");
            var str = $"{boxLevel.Level} * {boxLevel.Level}";
            await view.ScrollToAsync(this.buttons[str], ScrollToPosition.Center, false);
        }
        async void LevelSelect(string str)
        {
            mask.Hide();
            var level = str[0] - '0';
            if(boxLevel.Level != level)
            {
                boxLevel.Level = level;
                await SaveArgsAsync();
            }
            InitLayout();
        }

        void RestartClicked(object sender, EventArgs args)
        {
            InitLayout(true);
        }

        void OnAboutButtonClicked(object sender, EventArgs args)
        {
            if(!mask.Contains("about"))
            {
                var aboutView = ControlUtil.GetAbout(gameInfo);
                mask.Add("about", gameInfo.Name, aboutView);
            }
            mask.ShowWithCancel("about", "确定");
        }
        void OnRecordClicked(object sender, EventArgs args)
        {
            //var sorter = new Sorter<EightRecord>(q => q.OrderByDescending(x => x.Level).ThenBy(x => x.AllClickCount).ThenBy(x => x.Duration).ThenByDescending(x => x.ID));
            Navigation.PushAsync(new RecordPage(gameInfo.Name, new RecordViewModel<EightRecord>(null)));
        }

        private void TapToBack(object sender, EventArgs e)
        {
            Navigation.PopAsync();
        }
        protected override bool OnBackButtonPressed()
        {
            return true;
        }
    }
}